<?php
/**
 * Zhub - A PHP Framework For API Developer
 *
 * This file is part of the Zhub package.
 *
 * (c) YiLong Fan <fantiq@163.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @author fanyilong <fantiq@163.com>
 * @copyright 2017 fanyilong. All rights reserved.
 * @package Zhub
 * @date 18-3-5
 */

namespace Zhub\Route;


use Zhub\Route\Exceptions\RouteNotFoundException;

class Parser
{
    const PATTERN_END_FLAG = '$';

    const PATTERN_PARAM_FLAG = ':';

    const PATTERN_OPTIONAL_FLAG = '?';

    const PATTERN_URL_SEPARATOR = '/';

    private $tree = [];

    public function __construct()
    {
    }

    public function parse(RouteStruct $route)
    {
        $path = $route->getPath();
        $paths = strpos($path, self::PATTERN_OPTIONAL_FLAG) === false ?
            [$path] : $this->fission($path);
        /*
         * 这里存在的问题是 RouteEntity 对象在 fission后的路由有几个就对应几个... 这里应该 clone
         */
        foreach ($paths as $path) {
            $path = trim($path, '\/');
            $path = empty($path) ? '/' : $path;
            $p = &$this->tree;
            $params = [];
            foreach (explode(self::PATTERN_URL_SEPARATOR, $path) as $segment) {
                if (strpos($segment, self::PATTERN_PARAM_FLAG) === 0) {
                    $params[] = str_replace(self::PATTERN_PARAM_FLAG, '', $segment);
                    $segment = self::PATTERN_PARAM_FLAG;
                }
                elseif (!array_key_exists($segment, $p)) {
                    $p[$segment] = [];
                }
                $p = &$p[$segment];
            }
            $r = clone $route;
            $r->setParameters($params);
            $r->setPath($path);
            $p[self::PATTERN_END_FLAG] = $r;
        }
    }

    private function fission(string $path)
    {
        $path = trim($path, '\/');
        $paths = [''];
        foreach(explode(self::PATTERN_URL_SEPARATOR, $path) as $segment) {
            $size = count($paths);
            for($i = 0; $i < $size; $i++) {
                if (strpos($segment, self::PATTERN_OPTIONAL_FLAG) === strlen($segment) - 1) {
                    $paths[] = $paths[$i];
                }
                $paths[$i] .= trim($segment, self::PATTERN_OPTIONAL_FLAG) . '/';
            }
        }
        return $paths;
    }

    public function match(string $path):RouteStruct
    {
        $path = trim($path, '\/');
        $path = empty($path) ? '/' : $path;

        $p = &$this->tree;
        $params = [];
        foreach (explode(self::PATTERN_URL_SEPARATOR, $path) as $segment) {
            if (!array_key_exists($segment, $p)) {
                if (!array_key_exists(self::PATTERN_PARAM_FLAG, $p)) {
                    throw new RouteNotFoundException($path);
                }
                $params[] = $segment;
                $segment = self::PATTERN_PARAM_FLAG;
            }
            $p = &$p[$segment];
        }
        if(!array_key_exists(self::PATTERN_END_FLAG, $p)) {
            throw new RouteNotFoundException($path);
        }
        $route = $p[self::PATTERN_END_FLAG];
        if (! $route instanceof RouteStruct) {
            throw new RouteNotFoundException($path);
        }
        $paramsKey = $route->getParameters();
        $route->setParameters(array_combine($paramsKey, $params));
        return $route;
    }
    public function getRoutes():array
    {
        $p = &$this->tree;
        return $this->forward($p);
    }
    private function forward(&$p)
    {
        $routes = [];
        foreach ($p as $key => $val) {
            if ($key == '$') {

                $routes[] = $val;
                continue;
            }
            $p = &$val;
            $routes = array_merge($routes, $this->forward($p));
        }
        return $routes;
    }
}